package high.concurrent.service.impl;

import com.alibaba.fastjson.JSON;
import high.concurrent.constant.CacheKey;
import high.concurrent.enums.BizCodeEnum;
import high.concurrent.exception.BizException;
import high.concurrent.interceptor.UserInterceptor;
import high.concurrent.model.LoginUser;
import high.concurrent.request.AddCartRequest;
import high.concurrent.service.CartService;
import high.concurrent.service.ProductService;
import high.concurrent.utils.JsonData;
import high.concurrent.vo.CartItemVO;
import high.concurrent.vo.CartVO;
import high.concurrent.vo.ProductVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CartServiceImpl implements CartService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ProductService productService;

    /**
     * 添加购物车
     * @param addCartRequest   购物车信息
     * @return
     */
    @Override
    public JsonData addCart(AddCartRequest addCartRequest) {

        Long productId = addCartRequest.getProductId();
        Integer buyNum = addCartRequest.getBuyNum();

        // 通过用户购物车key，获取购物车信息
        BoundHashOperations<String, Object, Object> cartValue = this.getCartValue();

        CartItemVO cartItemVO = new CartItemVO();
        // 判断，如果购物车不存在该商品，则直接插入一条数据
        if(cartValue.get(String.valueOf(productId)) == null){

            // 根据商品id, 查询商品详情
            ProductVO productVO = (ProductVO) productService.getProductDetail(productId).getData();
            if(productVO == null){
                // 商品不存在直接抛异常
                throw new BizException(BizCodeEnum.CART_FAIL);
            }

            // 填写购物项信息
            cartItemVO.setAmount(productVO.getPrice());
            cartItemVO.setBuyNum(buyNum);
            cartItemVO.setProductId(productId);
            cartItemVO.setProductImg(productVO.getCoverImg());
            cartItemVO.setProductTitle(productVO.getTitle());
            cartValue.put(String.valueOf(productId), JSON.toJSONString(cartItemVO));

        }else{

            // 购物车存在，则直接改一下数量即可
            Object cartItemObj = cartValue.get(String.valueOf(productId));
            CartItemVO cartItemVO1 = JSON.parseObject(cartItemObj.toString(), CartItemVO.class);
            cartItemVO1.setBuyNum(cartItemVO1.getBuyNum() + buyNum);
            cartValue.put(String.valueOf(productId), JSON.toJSONString(cartItemVO1));
        }

        return JsonData.buildSuccess();
    }

    /**
     * 清空购物车
     * @return
     */
    @Override
    public void deleteCart() {

        // 获取该用户购物车keey
        String cartKey = getCartKey();
        redisTemplate.delete(cartKey);
    }


    /**
     * 查询购物车
     * @return
     */
    @Override
    public JsonData selectCart() {

        // 查询购物车得所有商品购物项, 参数false，标识无需获取最新价格
        List<CartItemVO> cartNewMsg = this.getCartNewMsg(false);

        // 把购物项保存到购物车信息，并返回
        CartVO cartVO = new CartVO();
        cartVO.setCartItems(cartNewMsg);

        return JsonData.buildSuccess(cartVO);
    }


    /**
     * 删除某个购物项
     * @param productId 商品id
     * @return
     */
    @Override
    public void deleteCartItem(Long productId) {

        // 获取购物车信息
        BoundHashOperations<String, Object, Object> cartValue = getCartValue();

        cartValue.delete(String.valueOf(productId));


    }


    /**
     * 修改某个购物项数量
     * @param  addCartRequest   商品id , 数量
     * @return
     */
    @Override
    public void updateCartItemCount(AddCartRequest addCartRequest) {

        // 获取购物车信息
        BoundHashOperations<String, Object, Object> cartValue = getCartValue();

        // 获取要加/减 数量的那个购物项信息
        Object cartItemObj = cartValue.get(String.valueOf(addCartRequest.getProductId()));
        if(cartItemObj == null){throw new BizException(BizCodeEnum.CART_FAIL);}

        // 把cartItemObj转成VO类型
        CartItemVO cartItemVO = JSON.parseObject(cartItemObj.toString(), CartItemVO.class);
        cartItemVO.setBuyNum(addCartRequest.getBuyNum());

        cartValue.put(String.valueOf(addCartRequest.getProductId()), JSON.toJSONString(cartItemVO));

    }

    /**
     * 获取购物车商品项的最新价格，并删除对应购物车商品
     * @param productIdList
     * @return
     */
    @Override
    public JsonData getCartItemLatestPrice(List<Long> productIdList) {

        // 获取该用户购物车下所有商品的最新信息
        List<CartItemVO> listCartItemVOs = getCartNewMsg(true);

        // 筛选出用户支付的那些商品,
        List<CartItemVO> selectCartItem = listCartItemVOs.stream().filter(item -> {
            if(productIdList.contains(item.getProductId())){
                // TODO 为了更安全，这里可以向购物车冻结表(暂时没创建)插入记录(一个人一个商品项就是一条数据)，
                //  然后再发送一个消息1-5分钟， 只要订单存在，不管什么状态，就算正常。否则把购物车数据还原到redis。
                //  这块思路和优惠券，库存一样
                // 删除购物车对应商品项， 因为已经支付完了，可以删掉了
                deleteCartItem(item.getProductId());
                return true;
            }else {
                return false;
            }
        }).collect(Collectors.toList());

        return JsonData.buildSuccess(selectCartItem);
    }

    /**
     * 获取购物车最新信息(价格和数据库对比)
     * @param latsetCartItemFlag   是否需要获取商品最新价格列表
     * @return
     */
    private List<CartItemVO> getCartNewMsg(boolean latsetCartItemFlag){

        // 获取购物车信息, 提炼出该购物车所有的商品
        BoundHashOperations<String, Object, Object> cartValue = getCartValue();
        List<Object> cartListObj = cartValue.values();

        // 遍历Object类型得购物车，并转型为List<CartVO>类型
        List<CartItemVO> listCartItemVOs = new ArrayList<>();
        for (Object cartItemObj : cartListObj){

            // 转型把Object类型，转为CartItemVO类型
            CartItemVO cartItemVO = JSON.parseObject(cartItemObj.toString(), CartItemVO.class);
            listCartItemVOs.add(cartItemVO);
        }

        // 如果需要获取最新数据(数据库的商品价格可能变了)
        if(latsetCartItemFlag){
            this.getCartLatestPrice(listCartItemVOs);
        }

        return listCartItemVOs;
    }


    /**
     * 获取商品得最新价格后得购物车信息
     * @param listCartItemVOs  购物项集合，传过来直接改，就不需要返回值了
     */
    private void getCartLatestPrice(List<CartItemVO> listCartItemVOs){

        // 提取出来每个购物项的商品id，组成集合
        List<Long> productList = listCartItemVOs.stream().map(CartItemVO::getProductId).collect(Collectors.toList());

        // 查询每个商品对应的最新价格信息
        List<ProductVO> listProductVOs = productService.selectInProductMsg(productList);

        // 根据productId进行分组，转成Map集合， 以productId作为key，ProductVO作为value
        Map<Long, ProductVO> productVoMap = listProductVOs.stream()
                .collect(Collectors.toMap(ProductVO::getId, Function.identity()));

        // 遍历原redis得购物项集合
        listCartItemVOs.stream().forEach(item ->{

            // 根据productId把获取productVO, 然后逐个属性替换掉旧集合内容
            ProductVO productVO = productVoMap.get(item.getProductId());
            item.setProductImg(productVO.getCoverImg());
            item.setAmount(productVO.getPrice());
            item.setProductTitle(productVO.getTitle());
        });
    }


    /**
     * 获取购物车信息(仅仅从redis获取)
     * @return
     */
    private BoundHashOperations<String, Object, Object> getCartValue(){

        String cartKey = getCartKey();
        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(cartKey);
        return boundHashOperations;
    }


    /**
     * 获取该用户对应的购物车key
     * @return
     */
    private String getCartKey(){

        // 获取用户信息
        LoginUser loginUser = UserInterceptor.threadLocal.get();

        String cartKey = String.format(CacheKey.CART_KEY, loginUser.getId());
        return cartKey;
    }
}
